/* Pontificia Universidad Javeriana - Facultad de Ingenier�a   */

package BESA.Agent;

import BESA.Agent.Event.EventBESA;
import BESA.SystemLevel.Directory.AgHandler;
import BESA.SystemLevel.transactions.agent.TransactionalEventBESA;
import BESA.SystemLevel.transactions.agent.TransactionalGuard;
import BESA.util.ReportBESA;
import java.util.ArrayList;

@SuppressWarnings("all")
final public class ChannelBESA implements Runnable {

	public ChannelBESA(AgentBESA ag) {
		this.ag = ag;
		this.mbox = new MBox();
		this.ports = new ArrayList();
		this.waitlogicChannel = false;
	}

	final public void run() {
		// Etapa de inicializacion del ambiente del agente
		ReportBESA.debug(10,
				"[ChannelBESA::run()] Arranca run del Agente con aid="
						+ ag.getAid());
		// Hacer setup de la estructura del agente
		ag.init();
		ag.setupAgent();
		// Arrancar hilos de los comportamientos - incluye sicronizacion de
		// barrera
		// Espera que se ejecuten los setups de los behaviors
		ag.startBehaviors();

		// Agente listo para trabajar -> Despertar al constructor del agente
		this.ag.signal();
		ReportBESA.debug(1,
				"[ChannelBESA::run()] DESPUES de signal al Agente con aid="
						+ ag.getAid());

		// Ciclo principal de procesamiento de eventos
		while (ag.isAlive().booleanValue()) {

			// Acceso synchronized en la variable alive
			if (ag.isAlive().booleanValue()) {
				// Recepcion bloqueante y procesamiento de un evento
				ReportBESA.debug(1,
						"[ChannelBESA::run()] Antes de receive - Duerme agente con aid="
								+ ag.getAid());
				EventBESA ev = mbox.receiveEvent();
				ReportBESA.debug(5,
						"[ChannelBESA::run()] Despues de receive - Despierta Agente con aid="
								+ ag.getAid() + " - Evento=" + ev.getStamp());
				// Se verifica estado logico del agente, si es true no se
				// procesa.
				if (this.waitlogicChannel == false
						|| ev.getType().equalsIgnoreCase("KILL_BESA_AGENT")) {
					// We check here if the event is a transactional one and
					// process it correctly
					if (ev instanceof TransactionalEventBESA) {
						
						

						AgHandler agBESA = ag.getAdmLocal().getHandlerByAid(ag.getAid());
						
						TransactionalEventBESA transEv = (TransactionalEventBESA) ev;
						Port port = this.findPort(ev.getType());
						GuardBESA gBESA = port.getGuard();
						TransactionalGuard transG =(TransactionalGuard)gBESA; 
						ag.getAdmLocal().getGcc().eventManager(agBESA, transEv, transG.getIntention());

					} else {
						this.processEvent(ev);
					}
				}
			}
		} // fin while alive
		// Rutina de cierre del agente - cerrar y liberar recursos
		ag.shutdownAgent();
		// Enviar se�al de sincronizaci�n de barrera para finalizar el agente
		ag.getChannel().signalBehBarrier();
		// Fin entonces agente a la basura
		ag = null;
	}

	final protected Port findPort(GuardBESA guard) {
		// Intentar encontrar puerto asociado a la guarda
		for (int i = 0; i < ports.size(); ++i) {
			Port iport = (Port) ports.get(i);
			GuardBESA iguard = (GuardBESA) iport.getGuard();
			// Validar si la guarda corresponde
			if (iguard.equals(guard)) {
				return iport;
			}
		}
		return null;
	}

	final protected Port addPort(GuardBESA guard) {
		// Crear puerto asociado a la guarda y meterlo en vector de puertos
		// Validar primero que no se ha creado un puerto para esta guarda
		Port port = this.findPort(guard.getEvType());
		if (port == null) {
			port = new Port(guard);
			ports.add(port);
			return port;
		} else {
			// Retorna falso si ya hay un puerto asociado al mismo tipo evento
			// de la guarda - Para cada tipo evento solo puede haber un
			// puerto con su respectiva guarda
			return null;
		}
	}

	final protected boolean removePort(GuardBESA guard) {
		// Eliminar puerto asociado a la guarda
		// Validar primero que se ha creado un puerto para esta guarda
		// y que no hay ningun comportamiento asociado al puerto
		Port port = this.findPort(guard);
		if (port != null) {
			if (port.getBehaviors().size() == 0) {
				// Se elimina la guarda del estado y el puerto
				port.getGuard().finalize();
				ports.remove(port);
				port = null;
			} else {
				// Retorna falso si no se pudo porque aun hay binds con
				// algun comportamiento
				return false;
			}
		}
		return true;
	}

	final protected void purgePorts(BehaviorBESA beh) {
		// Eliminar beh de los puertos en que aparezca asociado
		for (int i = 0; i < ports.size(); ++i) {
			Port iport = (Port) ports.get(i);
			ArrayList ibehaviors = iport.getBehaviors();
			ibehaviors.remove(beh);
			this.removePort(iport.getGuard());
		}

	}

	final private boolean processEvent(EventBESA ev) {
		// Transfiere ev al puerto correspondiente y llama a tryGuard
		// para intentar disparar la guarda asociada
		// Retorna falso si un puerto asociado a ev no ha sido creado

		// Encontrar puerto al que le interesa el tipo del evento a procesar
		Port port = this.findPort(ev.getType());
		if (port != null) {
			// Inicializar la referencia a la guarda a partir del nombre
			// y procesar el mensaje en el puerto respectivo
			ev.setGuard(port.getGuard());
			// Verificar la fuente de evento
			if (ev.getSource() != EventBESA.EV_SOURCE_BESA) {
				// Todo evento no BESA debe ser transferido al puerto
				port.putEventInQueue(ev);
			}
			// Tratar de disparar la guarda asociada al tipo del evento
			port.tryGuard();
			return true; // ev transferido al puerto asociado
		} else {
			return false; // ev no tiene un puerto asociado
		}
	}

	/**
	 * @return Returns the ports.
	 * @uml.property name="ports"
	 */
	final protected ArrayList getPorts() {
		return ports;
	}

	/**
	 * @return Returns the mbox.
	 * @uml.property name="mbox"
	 */
	final protected MBox getMbox() {
		return mbox;
	}

	final protected Port findPort(String evType) {
		// Intentar encontrar puerto con guarda asociada al tipo de evento
		for (int i = 0; i < ports.size(); ++i) {
			Port iport = (Port) ports.get(i);
			GuardBESA iguard = (GuardBESA) iport.getGuard();
			// Validar si el tipo del evento corresponde con el de
			// la guarda asociada al puerto
			if (iguard.getEvType().equals(evType)) {
				return iport;
			}
		}
		return null;
	}

	public synchronized void initBehBarrier(int counterValue) {
		this.barrierCounter = counterValue;
		// this.behCounter = new Integer(newValue);
	}

	public synchronized void waitBehBarrier() {
		while (barrierCounter > 0) {
			ReportBESA.debug(1,
					"[ChannelBESA::waitBehBarrier()] Antes del wait - barrierCounter="
							+ this.barrierCounter);
			try {
				this.wait();
			} catch (InterruptedException ie) {
				ReportBESA.error("[AgentaBESA::waitBehBarrier()]"
						+ ie.getMessage());
			}
			ReportBESA.debug(1,
					"[ChannelBESA::waitBehBarrier()] Despues del wait - barrierCounter="
							+ this.barrierCounter);
		}
	}

	public synchronized void signalBehBarrier() {
		try {
			ReportBESA.debug(1,
					"[ChannelBESA::signalBehBarrier()] Antes del notify - barrierCounter="
							+ this.barrierCounter);
			this.barrierCounter--;
			this.notify();
			ReportBESA.debug(1,
					"[ChannelBESA::signalBehBarrier()] Antes del notify - barrierCounter="
							+ this.barrierCounter);
		} catch (Exception e) {
			ReportBESA.error("[ChannelBESA::signalBehBarrier] "
					+ e.getMessage());
		}
	}

	// NUEVO, Bloqueo logico del agente para que durante la fase de movilidad
	// del agente o muerte, retornar
	// inhabilidad para procesar eventos dirigidos al agente
	public synchronized void esperar() {
		try {
			this.waitlogicChannel = true;
		} catch (Exception e) {
			ReportBESA.error("[ChannelBESA::esperar] " + e.getMessage());
		}
	}

	public int getbarrierCounter() {
		return barrierCounter;
	}

	/**
	 * @uml.property name="waitlogicChannel"
	 */
	private boolean waitlogicChannel; // Nuevo, indica si el canal esta
										// bloqueado de forma logica
	/**
	 * @uml.property name="ag"
	 * @uml.associationEnd inverse="channel:BESA.Agent.AgentBESA"
	 */
	private AgentBESA ag;
	/**
	 * @uml.property name="barrierCounter"
	 */
	private int barrierCounter;
	/**
	 * @uml.property name="mbox"
	 * @uml.associationEnd multiplicity="(1 1)"
	 */
	private MBox mbox;
	/**
	 * @uml.property name="mobile"
	 */
	private boolean mobile;
	/**
	 * @link aggregation
	 * @associates Port
	 * @uml.property name="ports"
	 * @uml.associationEnd multiplicity="(0 -1)" elementType="BESA.Agent.Port"
	 */
	private ArrayList ports;
}
